﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
using DevExpress.XtraGrid.Views.Grid;
using E4RPDevExpress10;
using E4RPPrintManager10;
using E4RPTools;
using HouseRental.Controls;
using HouseRental.DataSets;

namespace HouseRental
{
    public class Controller
    {
        private SqlConnection _baseConnection;
        private SqlConnection _houseRentalConnection;

        #region Public Events

        public event EventHandler<EventArgs> HouseRentalChange;

        public event EventHandler<EventArgs> EstimateStateChange;

        public event EventHandler<EventArgs> EstimateCloseEditors;

        public event EventHandler<EventArgs> EstimateShow;

        public event EventHandler<EventArgs> EstimateSaved;

        #endregion Public Events

        #region Public Properties

        public IWin32Window OwnerWindow { get; set; }

        public OrcamentoDataSet Estimate { get; set; }
        
        public string HouseRentalName { get; set; }

        public string HouseRentalId { get; set; }

        public bool EstimateChanged { get; set; }

        #endregion Properties

        #region Private Methods

        private void PublishAndShowErrorMessage(Exception e, string errorMessage)
        {
            ExceptionPublisher.Publish(ConfigurationManager.AppSettings["LogFile"], e);
            ExceptionPublisher.ShowErrorMessage(OwnerWindow, errorMessage, "Condomínio");
        }

        private void OnHouseRentalChange()
        {
            EventHandler<EventArgs> houseRentalChange = HouseRentalChange;
            if (houseRentalChange != null)
                houseRentalChange(this, new EventArgs());
        }

        private void OnEstimateShow()
        {
            EventHandler<EventArgs> estimateShow = EstimateShow;
            if (estimateShow != null)
                estimateShow(this, new EventArgs());
        }

        private void OnEstimateStateChange()
        {
            EventHandler<EventArgs> estimateStateChange = EstimateStateChange;
            if (estimateStateChange != null)
                estimateStateChange(this, new EventArgs());
        }

        private void OnEstimateSaved()
        {
            EventHandler<EventArgs> estimateSaved = EstimateSaved;
            if (estimateSaved != null)
                estimateSaved(this, new EventArgs());
        }

        private void OnEstimateCloseEditors()
        {
            EventHandler<EventArgs> estimateCloseEditors = EstimateCloseEditors;
            if (estimateCloseEditors != null)
                estimateCloseEditors(this, new EventArgs());
        }

        private void FixBaseConnection()
        {
            if (_baseConnection.State == ConnectionState.Open)
                _baseConnection.Close();
            _baseConnection.Open();
        }

        private bool LoginBase(string userName, string password)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = ConfigurationManager.AppSettings["NomeBaseDados"];
            var connectionString = String.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};MultipleActiveResultSets=True;", dataSource, databaseName, userName, password);
            try
            {
                _baseConnection = new SqlConnection(connectionString);
                _baseConnection.Open();
            }
            catch (Exception e)
            {
                PublishAndShowErrorMessage(e, "Não foi possível ligar à base de dados.");
                return false;
            }
            return true;
        }

        private bool LoginHouseRental(string houseRentalId)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = String.Format("PRI{0}", houseRentalId);
            var userName = ConfigurationManager.AppSettings["User"];
            var password = ConfigurationManager.AppSettings["Password"];
            var connectionString = String.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};MultipleActiveResultSets=True;", dataSource, databaseName, userName, password);
            try
            {
                _houseRentalConnection = new SqlConnection(connectionString);
                _houseRentalConnection.Open();
            }
            catch (Exception e)
            {
                PublishAndShowErrorMessage(e, "Não foi possível ligar à base de dados.");
                return false;
            }
            return true;
        }

        private void FixHouseRentalConnection()
        {
            if (_houseRentalConnection.State == ConnectionState.Open)
                _houseRentalConnection.Close();
            _houseRentalConnection.Open();
        }

        private OrcamentoDataSet GetEstimate(long estimateId)
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();            
            try
            {
                OrcamentoDataSet dataSet = Primavera.GetEstimate(sqlTransaction, estimateId);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o orçamento.");
                return new OrcamentoDataSet();
            }
        }

        private static void UpdateEstimateTotals(OrcamentoDataSet dataSet)
        {
            dataSet.Orcamento[0].Valor = 0;
            foreach (OrcamentoDataSet.ItemOrcamentoRow estimateItem in dataSet.ItemOrcamento)
            {
                if (estimateItem.RowState == DataRowState.Deleted)
                    continue;
                dataSet.Orcamento[0].Valor += estimateItem.Total;
            }
        }

        private static string HouseRentalConnectionString(string houseRentalId)
        {
            var dataSource = ConfigurationManager.AppSettings["FonteDados"];
            var databaseName = String.Format("PRI{0}", houseRentalId);
            var userName = ConfigurationManager.AppSettings["User"];
            var password = ConfigurationManager.AppSettings["Password"];            
            return string.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3};MultipleActiveResultSets=True;", dataSource, databaseName, userName, password);           
        }

        private bool LoginHouseRental(IDbConnection connection, string houseRentalId)
        {
            try
            {
                connection.Open();
            }
            catch (Exception e)
            {
                PublishAndShowErrorMessage(e, string.Format("Não foi possível ligar à base de dados do condomínio '{0}'.", houseRentalId));
                return false;
            }
            return true;
        }

        private ListaLayoutDataSet GetListLayouts(string list)
        {
            FixBaseConnection();
            var sqlTransaction = _baseConnection.BeginTransaction();
            try
            {
                ListaLayoutDataSet dataSet = Primavera.GetListLayouts(sqlTransaction, list);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de layouts da lista.");
                return new ListaLayoutDataSet();
            }
        }

        private static List<string> GetLayouts(ListaLayoutDataSet dataSet)
        {
            var layouts = new List<string>();
            foreach (var layout in dataSet.ListaLayout)
                layouts.Add(layout.Nome);
            return layouts;
        }

        private void SaveLayout(string lista, string nome, string xmlLayout)
        {
            FixBaseConnection();
            var sqlTransaction = _baseConnection.BeginTransaction();
            try
            {
                Primavera.SaveLayout(sqlTransaction, lista, nome, xmlLayout);
                sqlTransaction.Commit();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível gravar layout da lista.");
            }
        }

        private static void Equalize(OrcamentoDataSet.ItemOrcamentoRow estimateItem, decimal value)
        {
            estimateItem.Janeiro = value;
            estimateItem.Fevereiro = value;
            estimateItem.Marco = value;
            estimateItem.Abril = value;
            estimateItem.Maio = value;
            estimateItem.Junho = value;
            estimateItem.Julho = value;
            estimateItem.Agosto = value;
            estimateItem.Setembro = value;
            estimateItem.Outubro = value;
            estimateItem.Novembro = value;
            estimateItem.Dezembro = value;
            estimateItem.Total = value * 12;
        }

        private static void Distribute(OrcamentoDataSet.ItemOrcamentoRow estimateItem, decimal value)
        {
            estimateItem.Total = value;
            var monthValue = estimateItem.Total/12;
            estimateItem.Janeiro = monthValue;
            estimateItem.Fevereiro = monthValue;
            estimateItem.Marco = monthValue;
            estimateItem.Abril = monthValue;
            estimateItem.Maio = monthValue;
            estimateItem.Junho = monthValue;
            estimateItem.Julho = monthValue;
            estimateItem.Agosto = monthValue;
            estimateItem.Setembro = monthValue;
            estimateItem.Outubro = monthValue;
            estimateItem.Novembro = monthValue;
            estimateItem.Dezembro = monthValue;
        }

        private OrcamentoDataSet GetEstimate(string houseRentalId, long year)
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                OrcamentoDataSet dataSet = Primavera.GetEstimate(sqlTransaction, houseRentalId, year);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler o orçamento.");
                return new OrcamentoDataSet();
            }
        }

        private static OrcamentoDataSet.ItemOrcamentoRow GetLastEstimateItem(OrcamentoDataSet lastEstimate, string productReference)
        {
            foreach (var lastEstimateItem in lastEstimate.ItemOrcamento)
                if (lastEstimateItem.Referencia == productReference)
                    return lastEstimateItem;
            return null;
        }

        private void ApplyOnLastYear(OrcamentoDataSet lastEstimate, OrcamentoDataSet.ItemOrcamentoRow estimateItem, decimal percentage)
        {
            OrcamentoDataSet.ItemOrcamentoRow lastEstimateItem = GetLastEstimateItem(lastEstimate, estimateItem.Referencia);
            if (lastEstimateItem == null) return;
            estimateItem.Janeiro = lastEstimateItem.Janeiro + lastEstimateItem.Janeiro*percentage/100;
            estimateItem.Fevereiro = lastEstimateItem.Fevereiro + lastEstimateItem.Fevereiro * percentage / 100;
            estimateItem.Marco = lastEstimateItem.Marco + lastEstimateItem.Marco * percentage / 100;
            estimateItem.Abril = lastEstimateItem.Abril + lastEstimateItem.Abril * percentage / 100;
            estimateItem.Maio = lastEstimateItem.Maio + lastEstimateItem.Maio * percentage / 100;
            estimateItem.Junho = lastEstimateItem.Junho + lastEstimateItem.Junho * percentage / 100;
            estimateItem.Julho = lastEstimateItem.Julho + lastEstimateItem.Julho * percentage / 100;
            estimateItem.Agosto = lastEstimateItem.Agosto + lastEstimateItem.Agosto * percentage / 100;
            estimateItem.Setembro = lastEstimateItem.Setembro + lastEstimateItem.Setembro * percentage / 100;
            estimateItem.Outubro = lastEstimateItem.Outubro + lastEstimateItem.Outubro * percentage / 100;
            estimateItem.Novembro = lastEstimateItem.Novembro + lastEstimateItem.Novembro * percentage / 100;
            estimateItem.Dezembro = lastEstimateItem.Dezembro + lastEstimateItem.Dezembro * percentage / 100;            
            UpdateEstimateItemTotal(estimateItem);
        }

        private FamiliaDataSet Families()
        {
            if (Estimate.Orcamento[0].IsIdCondominioNull()) return new FamiliaDataSet();
            var houseRentalId = Estimate.Orcamento[0].IdCondominio;
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new FamiliaDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                FamiliaDataSet dataSet = Primavera.GetFamilies(sqlTransaction);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de famílias.");
                return new FamiliaDataSet();
            }
        }

        private ProdutoDataSet FamilyProducts(string family)
        {
            if (Estimate.Orcamento[0].IsIdCondominioNull()) return new ProdutoDataSet();
            var houseRentalId = Estimate.Orcamento[0].IdCondominio;
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new ProdutoDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                ProdutoDataSet dataSet = Primavera.GetProducts(sqlTransaction, family);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, String.Format("Não foi possível ler a lista de produtos da família '{0}'.", family));
                return new ProdutoDataSet();
            }
        }

        private void AddProduct(ProdutoDataSet products)
        {
            foreach (var product in products.Produto)
            {
                if (ExistsProduct(product.Referencia))
                    continue;
                var estimateItem = Estimate.ItemOrcamento.NewItemOrcamentoRow();
                estimateItem.Id = Estimate.Orcamento[0].Id;
                estimateItem.IdItem = GetNextEstimateItemId();
                estimateItem.Referencia = product.Referencia;
                if (!product.IsDescricaoNull()) estimateItem.Descricao = product.Descricao;
                estimateItem.Janeiro = 0;
                estimateItem.Fevereiro = 0;
                estimateItem.Marco = 0;
                estimateItem.Abril = 0;
                estimateItem.Maio = 0;
                estimateItem.Junho = 0;
                estimateItem.Julho = 0;
                estimateItem.Agosto = 0;
                estimateItem.Setembro = 0;
                estimateItem.Outubro = 0;
                estimateItem.Novembro = 0;
                estimateItem.Dezembro = 0;
                estimateItem.Total = 0;
                Estimate.ItemOrcamento.AddItemOrcamentoRow(estimateItem);
            }
        }

        private void AddFamilyProducts(string familyName)
        {
            ProdutoDataSet products = FamilyProducts(familyName);
            AddProduct(products);
        }

        private bool ExistsProduct(string referencia)
        {
            foreach (OrcamentoDataSet.ItemOrcamentoRow estimateItem in Estimate.ItemOrcamento)
            {
                if (estimateItem.RowState == DataRowState.Deleted)
                    continue;
                if (!estimateItem.IsReferenciaNull() && estimateItem.Referencia == referencia)
                    return true;
            }
            return false;
        }

        private static void ClearErrors(DataTable table)
        {
            foreach (DataRow row in table.Rows)
                row.ClearErrors();
        }

        private bool ExistsEstimate(long id, string houseRentalId, int year)
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                bool result =  Primavera.ExistsEstimate(sqlTransaction, id, houseRentalId, year);
                sqlTransaction.Commit();
                return result;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível validar se existe orçamento.");
                return false;
            }
        }

        public AnaliseOrcamentalDataSet GetEstimateAnalysis(string houseRentalId, long? year)
        {
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new AnaliseOrcamentalDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                AnaliseOrcamentalDataSet dataSet = Primavera.GetEstimateAnalysis(sqlTransaction, year);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, "Não foi possível ler a análise orçamental.");
                return new AnaliseOrcamentalDataSet();
            }
        }

        private void FillHouseRentalName(AnaliseOrcamentalDataSet dataSet)
        {
            var houseRental = HouseRentals();
            foreach (var estimate in dataSet.Orcamento)
                estimate.Condominio = houseRental.Condomínio.FindById(estimate.IdCondominio).Nome;
        }

        //private static List<string> GetDistinctHouseRentals(AnaliseOrcamentalDataSet dataSet)
        //{
        //    var houseRentalIds = new List<string>();
        //    foreach (AnaliseOrcamentalDataSet.OrcamentoRow estimate in dataSet.Orcamento)
        //        if (!houseRentalIds.Contains(estimate.IdCondominio))
        //            houseRentalIds.Add(estimate.IdCondominio);
        //    return houseRentalIds;
        //}

        private DocumentoDataSet GetDocuments(string houseRentalId, AnalysisFilter analysisFilter)
        {
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new DocumentoDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                DocumentoDataSet dataSet = Primavera.GetDocuments(sqlTransaction, analysisFilter);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, String.Format("Não foi possível ler a lista de documentos do condomínio '{0}'.", houseRentalId));
                return new DocumentoDataSet();
            }
        }

        private static AnaliseOrcamentalDataSet.OrcamentoRow GetEstimate(string houseRentalId, DocumentoDataSet.DocumentoRow document, AnaliseOrcamentalDataSet dataSet)
        {
            foreach(var estimate in dataSet.Orcamento)
            {
                if(estimate.IdCondominio != houseRentalId)
                    continue;
                if(estimate.Referencia != document.Artigo)
                    continue;
                if (estimate.Ano == document.DataDocumento.Year && estimate.Mes.Month == document.DataDocumento.Month)
                    return estimate;
            }
            var newEstimate = dataSet.Orcamento.NewOrcamentoRow();
            newEstimate.Id = 0;
            newEstimate.IdItem = 0;
            newEstimate.IdCondominio = houseRentalId;
            newEstimate.Condominio = "HouseRental";
            newEstimate.Ano = document.DataDocumento.Year;
            newEstimate.Numero = 0;
            newEstimate.Nome = "";
            newEstimate.Referencia = document.Artigo;
            newEstimate.Descricao = document.IsDescricaoArtigoNull() ? "" : document.DescricaoArtigo;
            newEstimate.Mes = new DateTime(document.DataDocumento.Year, document.DataDocumento.Month, 1);
            newEstimate.Orcamentado = 0;
            newEstimate.Real = 0;
            newEstimate.Desvio = 0;
            newEstimate.DesvioPercentagem = 0;
            dataSet.Orcamento.AddOrcamentoRow(newEstimate);
            return newEstimate;
        }

        private static decimal GetPercentage(AnaliseOrcamentalDataSet.OrcamentoRow estimate)
        {
            if (estimate.Desvio == 0)
                return 0;
            if (estimate.Orcamentado == 0)
                return 100;
            if (estimate.Real == 0)
                return -100;
            return estimate.Desvio * 100 / estimate.Orcamentado;
        }

        private void FillRealValue(string houseRentalId, AnaliseOrcamentalDataSet dataSet, AnalysisFilter analysisFilter)
        {
            var documents = GetDocuments(houseRentalId, analysisFilter);
            foreach(var document in documents.Documento)
            {
                var estimate = GetEstimate(houseRentalId, document, dataSet);
                dataSet.Documento.AddDocumentoRow(estimate.IdCondominio, estimate.Referencia, estimate.Mes, document.NumeroDocumento.ToString(), document.Valor, 
                    document.TipoDocumento, document.DataDocumento);
                estimate.Real += document.Valor;
                estimate.Desvio = estimate.Real - estimate.Orcamentado;
                estimate.DesvioPercentagem = GetPercentage(estimate);
            }            
        }

        //private void FillRealValue(AnaliseOrcamentalDataSet dataSet, long? year)
        //{
        //    var houseRentalIds = GetDistinctHouseRentals(dataSet);
        //    foreach (var houseRentalId in houseRentalIds)
        //        FillRealValue(houseRentalId, dataSet, year);
        //}

        private AnaliseOrcamentalDataSet EstimateAnalysis(string houseRentalId, AnalysisFilter analysisFilter)
        {
            AnaliseOrcamentalDataSet dataSet = GetEstimateAnalysis(houseRentalId, analysisFilter.Year);
            FillRealValue(houseRentalId, dataSet, analysisFilter);
            FillHouseRentalName(dataSet);
            return dataSet;
        }

        private AnaliseOrcamentalDataSet AllEstimateAnalysis(AnalysisFilter analysisFilter)
        {
            var houseRentals = HouseRentals();
            var dataSet = new AnaliseOrcamentalDataSet();
            foreach(var houseRental in houseRentals.Condomínio)
            {
                var houseRentalAnalysis = EstimateAnalysis(houseRental.Id, analysisFilter);
                dataSet.Merge(houseRentalAnalysis);
            }
            return dataSet;
        }

        private static string GetPrinterName()
        {
            var printerName = ConfigurationManager.AppSettings["PrinterName"];
            if (!string.IsNullOrEmpty(printerName))
                return printerName;
            var printManager = new PrintManager();
            return printManager.GetDefaultPrinterName();
        }

        private ImpressaoOrcamentoDataSet GetEstimatesOutput(string houseRentalId, long? year)
        {
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new ImpressaoOrcamentoDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                ImpressaoOrcamentoDataSet dataSet = Primavera.GetEstimates(sqlTransaction, year);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, "Não foi possível ler os orçamentos.");
                return new ImpressaoOrcamentoDataSet();
            }
        }

        private static void AddRealEstimateItems(DocumentoDataSet.DocumentoRow document, ImpressaoOrcamentoDataSet.OrcamentoRow estimate, ImpressaoOrcamentoDataSet dataSet)
        {
            var estimateItem = dataSet.ItemOrcamento.NewItemOrcamentoRow();
            estimateItem.IdCondominioNome = estimate.IdCondominioNome;
            estimateItem.TipoItem = document.TipoDocumento;
            estimateItem.Referencia = document.Artigo;
            estimateItem.NumeroDocumento = document.NumeroDocumento;
            estimateItem.SerieDocumento = document.SerieDocumento;
            estimateItem.EntidadeNome = document.EntidadeNome;
            if (!document.IsEntidadeMoradaNull()) estimateItem.EntidadeMorada = document.EntidadeMorada;
            if (!document.IsDescricaoArtigoNull()) estimateItem.Descricao = document.DescricaoArtigo;
            estimateItem.JaneiroEstimado = 0;
            estimateItem.JaneiroReal = document.DataDocumento.Month == 1 ? document.Valor : 0;
            estimateItem.FevereiroEstimado = 0;
            estimateItem.FevereiroReal = document.DataDocumento.Month == 2 ? document.Valor : 0;
            estimateItem.MarcoEstimado = 0;
            estimateItem.MarcoReal = document.DataDocumento.Month == 3 ? document.Valor : 0;
            estimateItem.AbrilEstimado = 0;
            estimateItem.AbrilReal = document.DataDocumento.Month == 4 ? document.Valor : 0;
            estimateItem.MaioEstimado = 0;
            estimateItem.MaioReal = document.DataDocumento.Month == 5 ? document.Valor : 0;
            estimateItem.JunhoEstimado = 0;
            estimateItem.JunhoReal = document.DataDocumento.Month == 6 ? document.Valor : 0;
            estimateItem.JulhoEstimado = 0;
            estimateItem.JulhoReal = document.DataDocumento.Month == 7 ? document.Valor : 0;
            estimateItem.AgostoEstimado = 0;
            estimateItem.AgostoReal = document.DataDocumento.Month == 8 ? document.Valor : 0;
            estimateItem.SetembroEstimado = 0;
            estimateItem.SetembroReal = document.DataDocumento.Month == 9 ? document.Valor : 0;
            estimateItem.OutubroEstimado = 0;
            estimateItem.OutubroReal = document.DataDocumento.Month == 10 ? document.Valor : 0;
            estimateItem.NovembroEstimado = 0;
            estimateItem.NovembroReal = document.DataDocumento.Month == 11 ? document.Valor : 0;
            estimateItem.DezembroEstimado = 0;
            estimateItem.DezembroReal = document.DataDocumento.Month == 12 ? document.Valor : 0;
            estimateItem.TotalEstimado = 0;
            estimateItem.TotalReal = estimateItem.JaneiroReal + estimateItem.FevereiroReal + estimateItem.MarcoReal + estimateItem.AbrilReal + estimateItem.MaioReal + 
                estimateItem.JunhoReal + estimateItem.JulhoReal + estimateItem.AgostoReal + estimateItem.SetembroReal + estimateItem.OutubroReal + estimateItem.NovembroReal +
                estimateItem.DezembroReal;
            dataSet.ItemOrcamento.AddItemOrcamentoRow(estimateItem);
        }

        private ImpressaoOrcamentoDataSet EstimateOutputDataSet(string houseRentalId, string houseRentalName, AnalysisFilter analysisFilter)
        {
            var dataSet = GetEstimatesOutput(houseRentalId, analysisFilter.Year);
            foreach (var estimate in dataSet.Orcamento)
            {
                estimate.Condominio = estimate.Condominio;
                if (!estimate.IsNumeroPortaNull()) estimate.NumeroPorta = estimate.NumeroPorta;
                if (!estimate.IsPortaNull()) estimate.Porta = estimate.Porta;
                if (!estimate.IsAndarNull()) estimate.Andar = estimate.Andar;  
                var documents = GetDocuments(houseRentalId, analysisFilter);
                foreach (var document in documents.Documento)
                    AddRealEstimateItems(document, estimate, dataSet);
            }            
            return dataSet;
        }

        private ImpressaoOrcamentoDataSet AllEstimateOutputDataSet(AnalysisFilter analysisFilter)
        {
            var houseRentals = HouseRentals();
            var dataSet = new ImpressaoOrcamentoDataSet();
            foreach (var houseRental in houseRentals.Condomínio)
            {
                var estimate = EstimateOutputDataSet(houseRental.Id, houseRental.Nome, analysisFilter);
                dataSet.Merge(estimate, true);
            }
            dataSet.AcceptChanges();
            return dataSet;
        }

        private static ImpressaoOrcamentoDataSet.TotalArtigoRow AddTotal(string reference, string description, ImpressaoOrcamentoDataSet dataSet)
        {
            var total = dataSet.TotalArtigo.NewTotalArtigoRow();
            total.Referencia = reference;
            total.Descricao = description;
            total.JaneiroEstimado = 0;
            total.JaneiroReal = 0;
            total.JaneiroDesvio = 0;
            total.FevereiroEstimado = 0;
            total.FevereiroReal = 0;
            total.FevereiroDesvio = 0;
            total.MarcoEstimado = 0;
            total.MarcoReal = 0;
            total.MarcoDesvio = 0;
            total.AbrilEstimado = 0;
            total.AbrilReal = 0;
            total.AbrilDesvio = 0;
            total.MaioEstimado = 0;
            total.MaioReal = 0;
            total.MaioDesvio = 0;
            total.JunhoEstimado = 0;
            total.JunhoReal = 0;
            total.JunhoDesvio = 0;
            total.JulhoEstimado = 0;
            total.JulhoReal = 0;
            total.JulhoDesvio = 0;
            total.AgostoEstimado = 0;
            total.AgostoReal = 0;
            total.AgostoDesvio = 0;
            total.SetembroEstimado = 0;
            total.SetembroReal = 0;
            total.SetembroDesvio = 0;
            total.OutubroEstimado = 0;
            total.OutubroReal = 0;
            total.OutubroDesvio = 0;
            total.NovembroEstimado = 0;
            total.NovembroReal = 0;
            total.NovembroDesvio = 0;
            total.DezembroEstimado = 0;
            total.DezembroReal = 0;
            total.DezembroDesvio = 0;
            total.TotalEstimado = 0;
            total.TotalReal = 0;
            total.TotalDesvio = 0;
            dataSet.TotalArtigo.AddTotalArtigoRow(total);
            return total;
        }

        private static void FillTotals(ImpressaoOrcamentoDataSet dataSet)
        {
            foreach (var estimateItem in dataSet.ItemOrcamento)
            {
                var total = dataSet.TotalArtigo.FindByReferencia(estimateItem.Referencia) ??
                            AddTotal(estimateItem.Referencia, estimateItem.IsDescricaoNull() ? "" : estimateItem.Descricao, dataSet);
                total.JaneiroReal += estimateItem.JaneiroReal;
                total.JaneiroDesvio = total.JaneiroReal - total.JaneiroEstimado;
                total.FevereiroReal += estimateItem.FevereiroReal;
                total.FevereiroDesvio = total.FevereiroReal - total.FevereiroEstimado;
                total.MarcoReal += estimateItem.MarcoReal;
                total.MarcoDesvio = total.MarcoReal - total.MarcoEstimado;
                total.AbrilReal += estimateItem.AbrilReal;
                total.AbrilDesvio = total.AbrilReal - total.AbrilEstimado;
                total.MaioReal += estimateItem.MaioReal;
                total.MaioDesvio = total.MaioReal - total.MaioEstimado;
                total.JunhoReal += estimateItem.JunhoReal;
                total.JunhoDesvio = total.JunhoReal - total.JunhoEstimado;
                total.JulhoReal += estimateItem.JulhoReal;
                total.JulhoDesvio = total.JulhoReal - total.JulhoEstimado;
                total.AgostoReal += estimateItem.AgostoReal;
                total.AgostoDesvio = total.AgostoReal - total.AgostoEstimado;
                total.SetembroReal += estimateItem.SetembroReal;
                total.SetembroDesvio = total.SetembroReal - total.SetembroEstimado;
                total.OutubroReal += estimateItem.OutubroReal;
                total.OutubroDesvio = total.OutubroReal - total.OutubroEstimado;
                total.NovembroReal += estimateItem.NovembroReal;
                total.NovembroDesvio = total.NovembroReal - total.NovembroEstimado;
                total.DezembroReal += estimateItem.DezembroReal;
                total.DezembroDesvio = total.DezembroReal - total.DezembroEstimado;
                total.TotalReal += estimateItem.TotalReal;
                total.TotalEstimado += estimateItem.TotalEstimado;
                total.TotalDesvio = total.TotalReal - total.TotalEstimado;
            }
        }

        private DataSet PrintDataSet(ReportFilter reportFilter)
        {
            if(!reportFilter.XsdFile.Equals("ImpressaoOrcamentoDataSet.xsd"))
                return new DataSet();
            var dataSet = reportFilter.AnalysisFilter.AllHouseRentals
                              ? AllEstimateOutputDataSet(reportFilter.AnalysisFilter)
                              : EstimateOutputDataSet(HouseRentalId, HouseRentalName, reportFilter.AnalysisFilter);
            FillTotals(dataSet);
            dataSet.AcceptChanges();
            return dataSet;
        }

        private ImpressaoOrcamentoDataSet GetPrintDataSet(string houseRentalId, string houseRentalName, AnalysisFilter analysisFilter)
        {
            var dataSet = EstimateOutputDataSet(houseRentalId, houseRentalName, analysisFilter);
            FillTotals(dataSet);
            dataSet.AcceptChanges();
            return dataSet;
        }

        private static void Print(DataSet dataSet, string reportFile)
        {
            var printManager = new PrintManager
            {
                NumberOfCopies = 1,
                PrinterName = GetPrinterName(),
                ReportFile = reportFile,
                DataSource = dataSet
            };
            printManager.Print();
        }

        
        #endregion

        public bool Login(string userName, string password)
        {
            return LoginBase(userName, password);
        }

        public void CloseConnection()
        {
            if (_houseRentalConnection != null) _houseRentalConnection.Close();
            _baseConnection.Close();
        }

        public OrcamentoDataSet Estimates()
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                OrcamentoDataSet dataSet = Primavera.GetEstimates(sqlTransaction, HouseRentalId);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de orçamentos.");
                return new OrcamentoDataSet();
            }
        }

        public bool ExistsOpenedEstimate(IWin32Window owner, string action)
        {
            if (Estimate == null)
                return true;
            OnEstimateCloseEditors();
            if (!Estimate.HasChanges())
                return true;
            DialogResult result = MessageBox.Show(owner, "Existe um orçamento por tratar." + Environment.NewLine + action,
                "Orçamento", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2);
            return result == DialogResult.Yes;
        }

        public void ShowEstimate(long estimateId)
        {
            Estimate = GetEstimate(estimateId);
            OnEstimateShow();
        }

        public CondominioDataSet HouseRentals()
        {
            FixBaseConnection();
            var sqlTransaction = _baseConnection.BeginTransaction();
            try
            {
                CondominioDataSet dataSet = Primavera.GetHouseRentals(sqlTransaction);
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de condomínios.");
                return new CondominioDataSet();
            }
        }

        public ProdutoDataSet Products(string houseRentalId)
        {
            if (houseRentalId == "") return new ProdutoDataSet();
            var connection = new SqlConnection(HouseRentalConnectionString(houseRentalId));
            if (!LoginHouseRental(connection, houseRentalId)) return new ProdutoDataSet();
            var sqlTransaction = connection.BeginTransaction();
            try
            {
                ProdutoDataSet dataSet = Primavera.GetProducts(sqlTransaction);
                sqlTransaction.Commit();
                connection.Close();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                connection.Close();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de produtos.");
                return new ProdutoDataSet();
            }                            
        }

        public void ChangeHouseRental()
        {
            var changeHouseRental = new ChangeHouseRental { HouseRentalData = HouseRentals() };
            if (changeHouseRental.ShowDialog() != DialogResult.OK)
                return;
            if (!LoginHouseRental(changeHouseRental.HouseRentalId))
                return;
            HouseRentalName = changeHouseRental.HouseRentalName;
            HouseRentalId = changeHouseRental.HouseRentalId;
            OnHouseRentalChange();
        }

        public void NewEstimate()
        {
            Estimate = new OrcamentoDataSet();
            Estimate.Orcamento.AddOrcamentoRow(-1, HouseRentalId, DateTime.Today.Year, 0, -1, "", EstimateState.Desbloqueado.ToString());
            OnEstimateShow();
        }        

        public Result IsEstimateValid()
        {
            var result = new Result();
            var estimate = Estimate.Orcamento[0];

            if (estimate.IsIdCondominioNull() || estimate.IdCondominio == "") result.Add("IdCondominio", "Por favor, preencha o condomínio.");
            if (estimate.IsAnoNull() || estimate.Ano == 0) result.Add("Ano", "Por favor, preencha o ano.");
            if (result.Errors.Count == 0 && ExistsEstimate(estimate.Id, estimate.IdCondominio, estimate.Ano))
                result.Add("Ano", "Por favor, indique outro ano dado que já existe um orçamento para o ano indicado.");
            var produtReferences = new List<string>();
            ClearErrors(Estimate.ItemOrcamento);
            foreach (var estimateItem in Estimate.ItemOrcamento)
            {
                if (estimateItem.RowState == DataRowState.Deleted)
                    continue;
                if (estimateItem.IsReferenciaNull() || estimateItem.Referencia == "")
                    estimateItem.SetColumnError("Referencia", "Por favor, preencha o produto.");
                if (produtReferences.Contains(estimateItem.Referencia))
                    estimateItem.SetColumnError("Referencia", "O produto está duplicado, por favor remova o produto.");
                else
                    produtReferences.Add(estimateItem.Referencia);
                if (estimateItem.IsTotalNull() || estimateItem.Total < 0)
                    estimateItem.SetColumnError("Total", "Por favor, o valor total não pode ser negativo.");
            }
            return result;
        }

        public void UpdateHouseRental(string houseRentalId)
        {
            Estimate.Orcamento[0].IdCondominio = houseRentalId;
            if (Estimate.ItemOrcamento.Count == 0) return;
            var toDelete = new List<OrcamentoDataSet.ItemOrcamentoRow>();
            foreach(var estimateItem in Estimate.ItemOrcamento)
                toDelete.Add(estimateItem);
            foreach (var estimateItem in toDelete)
                estimateItem.Delete();
        }

        public void UpdateEstimateItemTotal(OrcamentoDataSet.ItemOrcamentoRow estimateItem)
        {
            estimateItem.Total = estimateItem.Janeiro + estimateItem.Fevereiro + estimateItem.Marco + estimateItem.Abril + estimateItem.Maio + estimateItem.Junho + 
                estimateItem.Julho + estimateItem.Agosto + estimateItem.Setembro + estimateItem.Outubro + estimateItem.Novembro + estimateItem.Dezembro;                
        }

        public long GetNextEstimateItemId()
        {
            long itemId = 0;
            foreach (OrcamentoDataSet.ItemOrcamentoRow estimateItem in Estimate.ItemOrcamento)
            {
                if (estimateItem.RowState == DataRowState.Deleted)
                    continue;
                if (itemId > estimateItem.IdItem)
                    itemId = estimateItem.IdItem;
            }
            return itemId - 1;
        }

        public void SaveEstimate(IWin32Window owner)
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                UpdateEstimateTotals(Estimate);
                Primavera.SaveEstimate(sqlTransaction, Estimate.Orcamento[0]);
                Primavera.UpdateEstimateItems(sqlTransaction, Estimate.ItemOrcamento);
                sqlTransaction.Commit();
                OnEstimateSaved();
                NewEstimate();
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível gravar o orçamento.");
            }
        }

        public void Equalize(List<OrcamentoDataSet.ItemOrcamentoRow> estimateItems, decimal value)
        {
            foreach (var estimateItem in estimateItems)
                Equalize(estimateItem, value);
        }

        public void Distribute(List<OrcamentoDataSet.ItemOrcamentoRow> estimateItems, decimal value)
        {
            foreach (var estimateItem in estimateItems)
                Distribute(estimateItem, value);
        }

        public void ApplyOnLastYear(List<OrcamentoDataSet.ItemOrcamentoRow> estimateItems)
        {
            if (estimateItems.Count == 0)
                return;
            var percentageForm = new PercentageForm();
            if (percentageForm.ShowDialog() != DialogResult.OK)
                return;
            OrcamentoDataSet lastEstimate = GetEstimate(estimateItems[0].OrcamentoRow.IdCondominio, estimateItems[0].OrcamentoRow.Ano-1);
            foreach (var estimateItem in estimateItems)
                ApplyOnLastYear(lastEstimate, estimateItem, percentageForm.Percentage);
        }        

        public void AddProducts()
        {
            ProdutoDataSet products = Products(HouseRentalId);
            AddProduct(products);
        }

        public void AddFamilyProducts()
        {
            var family = new Family { FamilyData = Families() };
            if (family.ShowDialog() != DialogResult.OK)
                return;
            AddFamilyProducts(family.FamilyName);
        }

        public void LockEstimate()
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                Primavera.UpdateEstimateState(sqlTransaction, Estimate.Orcamento[0].Id, EstimateState.Bloqueado.ToString());
                sqlTransaction.Commit();
                Estimate.Orcamento[0].Estado = EstimateState.Bloqueado.ToString();
                Estimate.AcceptChanges();
                OnEstimateStateChange();                
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível bloquear o orçamento.");
            }
        }

        public void UnLockEstimate()
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                Primavera.UpdateEstimateState(sqlTransaction, Estimate.Orcamento[0].Id, EstimateState.Desbloqueado.ToString());
                sqlTransaction.Commit();
                Estimate.Orcamento[0].Estado = EstimateState.Desbloqueado.ToString();
                Estimate.AcceptChanges();
                OnEstimateStateChange();                
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível desbloquear o orçamento.");
            }
        }

        public string LoadLayout(GridView gridView, string list)
        {
            try
            {
                ListaLayoutDataSet dataSet = GetListLayouts(list);
                var form = new LoadLayoutXtraForm { Layouts = GetLayouts(dataSet) };
                if (form.ShowDialog() != DialogResult.OK)
                    return "";
                string xmlLayout = dataSet.ListaLayout.FindByListaNome(list, form.LayoutName).XmlLayout;
                DevExpressHelper.RestoreLayoutFromXml(gridView, xmlLayout);
                return form.LayoutName;
            }
            catch (Exception)
            {
                return "";
            }
        }

        public string SaveLayout(IWin32Window owner, string list, string xmlLayout)
        {
            try
            {
                var dataSet = GetListLayouts(list);
                var form = new SaveLayoutXtraForm { OwnerWindow = owner, Layouts = GetLayouts(dataSet) };
                if (form.ShowDialog() != DialogResult.OK)
                    return "";
                SaveLayout(list, form.LayoutName, xmlLayout);
                return form.LayoutName;
            }
            catch (Exception)
            {
                return "";
            }
        }

        public AnaliseOrcamentalDataSet EstimateAnalysis(AnalysisFilter analysisFilter)
        {
            return analysisFilter.AllHouseRentals ? AllEstimateAnalysis(analysisFilter) : EstimateAnalysis(HouseRentalId, analysisFilter);
        }

        public Collection<string> GetYears()
        {
            FixHouseRentalConnection();
            var sqlTransaction = _houseRentalConnection.BeginTransaction();
            try
            {
                var years = Primavera.GetYears(sqlTransaction);
                years.Add("Todos");
                sqlTransaction.Commit();
                return years;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível obter a lista dos distintos anos dos orçamentos.");
                return new Collection<string>();
            }
        }

        public RelatorioDataSet GetDataSetReports()
        {
            FixBaseConnection();
            var sqlTransaction = _baseConnection.BeginTransaction();
            try
            {
                RelatorioDataSet dataSet = Primavera.GetReports(sqlTransaction, "DataSet");
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de relatórios do tipo 'DataSet'.");
                return new RelatorioDataSet();
            }
        }

        public RelatorioDataSet GetDatabaseBeginDateEndDateReports()
        {
            FixBaseConnection();
            var sqlTransaction = _baseConnection.BeginTransaction();
            try
            {
                RelatorioDataSet dataSet = Primavera.GetReports(sqlTransaction, "DatabaseBeginDateEndDate");
                sqlTransaction.Commit();
                return dataSet;
            }
            catch (Exception e)
            {
                sqlTransaction.Rollback();
                PublishAndShowErrorMessage(e, "Não foi possível ler a lista de relatórios do tipo 'DatabaseBeginDateEndDate'.");
                return new RelatorioDataSet();
            }
        }

        public void Print(ReportFilter reportFilter)
        {
            if (!reportFilter.AnalysisFilter.BreakHouseRentals)
                Print(PrintDataSet(reportFilter), reportFilter.ReportFile);
            else
                PrintBreakingHouseRentals(reportFilter);
        }

        private void PrintBreakingHouseRentals(ReportFilter reportFilter)
        {
            if(!reportFilter.AnalysisFilter.AllHouseRentals)
            {
                var dataSet = GetPrintDataSet(HouseRentalId, HouseRentalName, reportFilter.AnalysisFilter);
                Print(dataSet, reportFilter.ReportFile);
                return;
            }
            var houseRentals = HouseRentals();
            foreach (var houseRental in houseRentals.Condomínio)
            {
                var dataSet = GetPrintDataSet(houseRental.Id, houseRental.Nome, reportFilter.AnalysisFilter);
                Print(dataSet, reportFilter.ReportFile);
            }
        }

        public void PrintPreview(ReportFilter reportFilter)
        {
            var form = new PrintPreviewXtraForm();
            form.Setup(PrintDataSet(reportFilter), reportFilter.ReportFile);
            form.ShowDialog(); 
        }

        public void PrintDatabase(string reportFile, DateTime begin, DateTime end)
        {
            var printManager = new PrintManager
            {
                NumberOfCopies = 1,
                PrinterName = GetPrinterName(),
                ReportFile = reportFile,
            };
            var user = ConfigurationManager.AppSettings["User"];
            var password = ConfigurationManager.AppSettings["Password"];
            var server = ConfigurationManager.AppSettings["FonteDados"];
            var database = "PRI" + HouseRentalId;
            var parameters = new List<PrintManagerParameter>
                                 {
                                     new PrintManagerParameter("DataInicio", begin),
                                     new PrintManagerParameter("DataFim", end)
                                 };
            printManager.PrintDatabaseLogon(user, password, server, database, parameters.ToArray());
        }

        public void PrintPreviewDatabase(string reportFile, DateTime begin, DateTime end)
        {
            var printManager = new PrintManager
            {
                NumberOfCopies = 1,
                PrinterName = GetPrinterName(),
                ReportFile = reportFile,
            };
            var user = ConfigurationManager.AppSettings["User"];
            var password = ConfigurationManager.AppSettings["Password"];
            var server = ConfigurationManager.AppSettings["FonteDados"];
            var database = "PRI" + HouseRentalId;
            var parameters = new List<PrintManagerParameter>
                                 {
                                     new PrintManagerParameter("DataInicio", begin),
                                     new PrintManagerParameter("DataFim", end)
                                 };
            printManager.PrintPreviewDatabaseLogon(user, password, server, database, parameters.ToArray());
        }
        
    }
}
